import QtQuick 2.0
import Qt.labs.settings 1.0
import Qt.labs.platform 1.1
import FileIO 1.0

import "components"

Item {
    id: installer
    readonly property string homePath: FileIO.userHomePath()
    readonly property string defaultGamePath: "drive_c/Program Files/Genshin Impact/Genshin Impact Game"
    readonly property string defaultLocation: `${homePath}/.wine/${defaultGamePath}`
    readonly property string configIni: "config.ini"
    readonly property string versionNotInstalled: "Not installed"
    readonly property var possibleLocations: [
        `.wine*/${defaultGamePath}`,
        `.steam/steam/steamapps/common/Proton */dist/share/default_pfx/${defaultGamePath}`
    ]

    property string patchRepo: "https://notabug.org/Krock/dawn"

    property alias installedVersion: localVersionLoader.version
    property string availableVersion
    property string preDownloadVersion

    property bool isPreDownloading: false
    property bool quitAfterCancel: false

    anchors.fill: parent

    InstallerUi {
        id: ui
        anchors.fill: parent

        progressText: clientInstaller.progressText
        progress: clientInstaller.progressPercent / 100.0

        onBrowseClicked: {
            fileDialog.open()
        }
        onInstallClicked: {
            ui.uiState = ui.uiStateInstalling
            dependenciesInstaller.check()
        }
        onCancelConfirmed: {
            clientInstaller.cancel()

            ui.log(qsTr("Canceled"))
            ui.log(qsTr(""))
            revertUiState()

            if (quitAfterCancel) {
                quitAfterCancel = false
                window.close()
            }
        }
        onCancelRejected: {
            quitAfterCancel = false
        }
        onPreDownloadClicked: {
            isPreDownloading = true
            dependenciesInstaller.check()
        }
        onUpdateClicked: {
            ui.uiState = ui.uiStateUpdating
            dependenciesInstaller.check()
        }
        onRetryClicked: checkForUpdates()
        onRepatchClicked: clientInstaller.reapplyWinePatch()
        onStartClicked: {
            ui.uiState = ui.uiStateStarting
            clientLauncher.start()
        }
        onStopConfirmed: {
            ui.uiState = ui.uiStateStopping
            clientLauncher.stop()
        }
        onKillWineServerClicked: clientLauncher.killWineServer()
        onCheckSelfUpdateClicked: selfUpdate.check()
    }

    Settings {
        id: settings
        category: "game"

        property string gamePath: ""
        property alias isLogVisible: ui.isLogVisible
        property bool multiConnections: false
        property bool limitDownload: false
        property string limitDownloadValue: "1M"
        property bool hideBackground: false
        property bool firstRun: true
    }

    LocalVersionLoader {
        id: localVersionLoader
        iniFilePath: settings.gamePath + "/" + configIni
    }

    WebVersionLoader {
        id: webVersionLoader

        onJsonChanged: tryCheckVersion()
        onError: {
            ui.showLog()
            ui.uiState = ui.uiStateRetry
            ui.log(qsTr("Failed to check version info. Check your Internet connection and try again."))
        }
    }

    DependenciesInstaller {
        id: dependenciesInstaller
        anchors.fill: parent

        onDependenciesInstallCanceled: {
            isPreDownloading = false
            ui.uiState = ui.uiStateInstall
        }
        onDependenciesInstallError: {
            ui.showLog()
            ui.log(qsTr("Failed to install dependencies"))
            revertUiState()
        }
        onDependenciesInstalled: startInstallUpdate()
    }

    ClientInstaller {
        id: clientInstaller
        anchors.fill: parent

        onPredownloadFinished: {
            isPreDownloading = false
            ui.hasPreDownload = false
        }

        onInstallError: {
            ui.showLog()
            switch (ui.uiState) {
            case ui.uiStateInstalling:
                ui.uiState = ui.uiStateInstall
                ui.log(qsTr("Error installing client"))
                return
            case ui.uiStateUpdating:
                ui.log(qsTr("Error updating client"))
                break
            }
            ui.log(qsTr(""))
            revertUiState()
        }

        onInstallCanceled: {
            switch (ui.uiState) {
            case ui.uiStateInstalling:
                ui.log(qsTr("Installation canceled"))
                return
            case ui.uiStateUpdating:
                ui.log(qsTr("Update canceled"))
                break
            }
            ui.log(qsTr(""))
            revertUiState()
        }

        onInstallFinished: {
            switch (ui.uiState) {
            case ui.uiStateInstalling:
                ui.log(qsTr("Installation completed!"))
                break
            case ui.uiStateUpdating:
                ui.log(qsTr("Update completed!"))
                break
            }
            ui.log(qsTr(""))
            checkFinished()
            isPreDownloading = false
        }

        onInstallSelectLanguagesCanceled: {
            revertUiState()
        }
    }

    ClientLauncher {
        id: clientLauncher

        onIsRunningChanged: {
            if (isRunning) {
                ui.uiState = ui.uiStateRunning
            } else {
                checkFinished()
            }
        }
    }

    SelfUpdate {
        id: selfUpdate
        anchors.fill: parent
    }

    FolderDialog {
        id: fileDialog
        title: qsTr(`Please choose 'Genshin Impact Game' directory`)
        folder: "file://" + (settings.gamePath || shortcuts.home)
        onAccepted: {
            let path = fileDialog.folder.toString().replace("file://", "")

            settings.gamePath = path
            ui.clearLog()

            if (!bootstrap()) {
                showInstallState()
            }
        }
    }

    function checkFinished() {
        ui.hasUpdate = !!availableVersion && availableVersion !== installedVersion
        if (clientInstaller.isPatchInstalled()) {
            ui.uiState = ui.uiStateStart
        } else {
            ui.uiState = ui.uiStatePatch
        }
    }

    function tryCheckVersion() {
        if (!webVersionLoader.json) {
            return
        }

        let game = webVersionLoader.json.data.game
        availableVersion = game.latest.version
        ui.log(qsTr("Web Version:") + " " + availableVersion)

        ui.hasUpdate = availableVersion !== installedVersion
        if (!ui.hasUpdate) {
            ui.log(qsTr("The game is up to date!"))
            checkFinished()

            let existingPreDownloadedVersion = clientInstaller.getPreDownloadedVersion()
            let preDownload = webVersionLoader.json.data.pre_download_game
            let hasPreDownload = preDownload && preDownload.latest && preDownload.latest.version || false
            if (hasPreDownload) {
                let latestPreDownloadVersion = preDownload.latest.version
                if (latestPreDownloadVersion !== existingPreDownloadedVersion) {
                    preDownloadVersion = preDownload.latest.version
                    ui.log(qsTr("Pre-download available") + " " + latestPreDownloadVersion)
                } else {
                    ui.log(qsTr("The version") + " " + existingPreDownloadedVersion + " " + qsTr("is pre-downloaded"))
                    hasPreDownload = false
                }
            }
            ui.hasPreDownload = hasPreDownload

            return
        }


        ui.log(qsTr("Update available"))

        switch (ui.uiState) {
            case ui.uiStateInstalling:
                clientInstaller.start()
                break
            case ui.uiStateLoading:
                checkFinished()
                break
        }
    }

    function tryLoadGameSettings() {
        if (!settings.gamePath) {
            console.debug("settings.gamePath is not set")
            return false
        }

        ui.log(qsTr("Game path: ") + settings.gamePath)

        const gameConfigPath = settings.gamePath + "/" + configIni

        if (!FileIO.isFileExists(gameConfigPath)) {
            console.debug(`${gameConfigPath} doesn't exist`)
            return false
        }

        localVersionLoader.load()

        return true
    }

    function checkForUpdates() {
        ui.log(qsTr("Installed Version:") + " " + installedVersion)
        ui.log(qsTr("Checking for updates..."))
        ui.uiState = ui.uiStateLoading

        webVersionLoader.loadVersion()
    }

    function detectLocation() {
        let foundPath
        for (let location of possibleLocations) {
            let foundLocations = FileIO.matchByWildcard(homePath + "/" + location + "/" + configIni)
            var foundLocation = foundLocations[0]
            if (foundLocation) {
                foundPath = foundLocation.substr(0, foundLocation.length - configIni.length)
                break
            }
        }

        return foundPath
    }

    function bootstrap() {
        if (tryLoadGameSettings()) {
            if (!FileIO.isFileExists(settings.gamePath + "/" + "GenshinImpact.exe")) {
                ui.log(qsTr("⚠ Wrong directory set as game directory."))
                ui.log(qsTr("⚠ Please choose 'Genshin Imact Game' dir, not the 'Genshin Impact'!"))
                ui.uiState = ui.uiStateWrongDir
                return true
            }

            if (!localVersionLoader.version) {
                ui.log(qsTr("⚠ Can't read game_version from config.ini, update wouldn't be possible."))
                checkFinished()
                return true
            }
            checkForUpdates()
            return true
        }

        return false
    }

    function handleAppExit() {
        if (!clientInstaller.isActive) {
            return true
        }

        quitAfterCancel = true
        ui.confirmCancelInstall()
        return false
    }

    function revertUiState() {
        switch (ui.uiState) {
            case ui.uiStateInstalling:
                ui.uiState = ui.uiStateInstall
                break
            case ui.uiStateInstall:
                break
            default:
                checkFinished()
        }
        isPreDownloading = false
    }

    function startInstallUpdate() {
        if (!webVersionLoader.json && !webVersionLoader.isLoading) {
            webVersionLoader.loadVersion()
            return
        }
        clientInstaller.start()
    }

    function showInstallState() {
        // in case we have empty directory as game path
        ui.log(qsTr("Install to specified location or press 'Browse...' to browse for game dir"))
        ui.uiState = ui.uiStateInstall
    }

    Component.onCompleted: {
        ui.log(qsTr("Please refrain from sharing this project in public."))
        ui.log("")

        if (bootstrap()) {
            return
        }

        if (settings.gamePath) {
            showInstallState()
            return
        }

        let foundPath = detectLocation()
        if (foundPath) {
            ui.log(qsTr("Location detected:") + " " + foundPath)
            settings.gamePath = foundPath
            if (bootstrap()) {
                return
            }
        }

        if (!foundPath) {
            ui.log(qsTr("No previously installed game detected."))
        } else {
            ui.log(qsTr("Failed to load game config."))
        }

        ui.log(qsTr("Install to default location or press 'Browse...' to browse for game dir"))
        ui.log(qsTr("Location to install:"))
        ui.log(defaultLocation)
        settings.gamePath = defaultLocation
        ui.uiState = ui.uiStateInstall
    }
}
